/**
 * @file    ble_mesh_node.c
 * @author  Azolla (1228449928@qq.com)
 * @brief   ble mesh node device
 * @version 0.1
 * @date    2023-06-01
 * 
 * @copyright Copyright (c) 2023
 * */
#include <stdio.h>
#include <string.h>
#include <inttypes.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "freertos/queue.h"  
#include "esp_mac.h"
#include "esp_log.h"
#include "esp_bt.h"
#include "esp_bt_device.h"
#include "esp_ble_mesh_defs.h"
#include "esp_ble_mesh_common_api.h"
#include "esp_ble_mesh_networking_api.h"
#include "esp_ble_mesh_provisioning_api.h"
#include "esp_ble_mesh_config_model_api.h"
#include "esp_ble_mesh_health_model_api.h"
#include "esp_ble_mesh_generic_model_api.h"
#include "esp_ble_mesh_lighting_model_api.h"
#include "esp_ble_mesh_time_scene_model_api.h"
#include "esp_ble_mesh_local_data_operation_api.h"
#include "sdkconfig.h"

#ifdef CONFIG_BLE_MESH_NODE
#include "hal_config.h"
#include "ble_nvs.h"
#include "ble_mesh_node.h"

 
#define TAG "ble_mesh_node"

#define CONFIG_MESH_MODEL_VND_SRV           1       // VND自定义模型
// #define CONFIG_MESH_MODEL_HSL_SRV           1       // 灯光HSL模型
// #define CONFIG_MESH_MODEL_CTL_SRV           1       // 灯光CTL色温模型  
// #define CONFIG_MESH_MODEL_LIGHT_SRV         1

#define PROV_OWN_ADDR   0x0001

#define LED_ON   1
#define LED_OFF  0
//============================================================================================
//============================================================================================
/* FreeRTOS event group to signal when we are connected & ready to make a request */
static const uint8_t NESH_SEND_COMP_EVT  = BIT0;    
static const uint8_t NESH_SEND_ERROR_EVT = BIT1; 
static const uint8_t NESH_PROV_OPEN_EVT  = BIT2;     
static const uint8_t NESH_PROV_CLOSE_EVT = BIT3;
static const uint8_t NESH_PROV_COMP_EVT  = BIT4;
static const uint8_t NESH_PROV_DEINIT_EVT= BIT7;
static EventGroupHandle_t xEvent = NULL;  
static QueueHandle_t xQueue = NULL;
 
static mesh_nvs_store_t store = { 0 };

static uint8_t dev_uuid[ESP_BLE_MESH_OCTET16_LEN] = {
    'Y', 'i', CONFIG_DEV_PID, 0x00,     // PID: Yi0000
    0xE5, 0x02,     // CID (fixed 16bit)
    0x80,           // MASK; Bit7: Support OTA;  Bit[0:6]: reserved 
    0x50,           // BLE_VERSION; 0x42：BLE4.2; 0x50：BLE5.0 above
    0x12, 0x34,     // ChipId (16bit)
    0x11, 0x22, 0x33, 0x44, 0x55, 0x66  // MAC Address  
};

static esp_ble_mesh_cfg_srv_t config_server = {
    .relay = ESP_BLE_MESH_RELAY_ENABLED,
    .beacon = ESP_BLE_MESH_BEACON_ENABLED,
#if defined(CONFIG_BLE_MESH_FRIEND)
    .friend_state = ESP_BLE_MESH_FRIEND_ENABLED,
#else
    .friend_state = ESP_BLE_MESH_FRIEND_NOT_SUPPORTED,
#endif
#if defined(CONFIG_BLE_MESH_GATT_PROXY_SERVER)
    .gatt_proxy = ESP_BLE_MESH_GATT_PROXY_ENABLED,
#else
    .gatt_proxy = ESP_BLE_MESH_GATT_PROXY_NOT_SUPPORTED,
#endif
    .default_ttl = 2,
    /* 3 transmissions with 20ms interval */
    .net_transmit = ESP_BLE_MESH_TRANSMIT(3, 30),
    .relay_retransmit = ESP_BLE_MESH_TRANSMIT(3, 30),
};

ESP_BLE_MESH_MODEL_PUB_DEFINE(onoff_pub, 2 + 3, ROLE_NODE);
static esp_ble_mesh_gen_onoff_srv_t onoff_server = {
    .rsp_ctrl.get_auto_rsp = ESP_BLE_MESH_SERVER_RSP_BY_APP,
    .rsp_ctrl.set_auto_rsp = ESP_BLE_MESH_SERVER_AUTO_RSP,
}; 
 
#ifdef CONFIG_MESH_MODEL_HSL_SRV
/* Light HSL state related context */
static esp_ble_mesh_light_hsl_state_t hsl_state;

/* Light HSL Server related context */
ESP_BLE_MESH_MODEL_PUB_DEFINE(hsl_pub, 2 + 9, ROLE_NODE);
static esp_ble_mesh_light_hsl_srv_t hsl_server = {
    .rsp_ctrl.get_auto_rsp = ESP_BLE_MESH_SERVER_RSP_BY_APP,
    .rsp_ctrl.set_auto_rsp = ESP_BLE_MESH_SERVER_AUTO_RSP,
    .state = &hsl_state,
};

/* Light HSL Setup Server related context */
ESP_BLE_MESH_MODEL_PUB_DEFINE(hsl_setup_pub, 2 + 6, ROLE_NODE);
static esp_ble_mesh_light_hsl_setup_srv_t hsl_setup_server = {
    .rsp_ctrl.get_auto_rsp = ESP_BLE_MESH_SERVER_AUTO_RSP,
    .rsp_ctrl.set_auto_rsp = ESP_BLE_MESH_SERVER_AUTO_RSP,
    .state = &hsl_state,
};
#endif  /* CONFIG_MESH_MODEL_HSL_SRV */

#ifdef CONFIG_MESH_MODEL_CTL_SRV
/* Light CTL state related context */
static esp_ble_mesh_light_ctl_state_t ctl_state;

/* Light CTL Server related context */
ESP_BLE_MESH_MODEL_PUB_DEFINE(ctl_pub, 2 + 9, ROLE_NODE);
static esp_ble_mesh_light_ctl_srv_t ctl_server = {
    .rsp_ctrl.get_auto_rsp = ESP_BLE_MESH_SERVER_RSP_BY_APP,
    .rsp_ctrl.set_auto_rsp = ESP_BLE_MESH_SERVER_AUTO_RSP,
    .state = &ctl_state,
};

/* Light CTL Setup Server related context */
ESP_BLE_MESH_MODEL_PUB_DEFINE(ctl_setup_pub, 2 + 6, ROLE_NODE);
static esp_ble_mesh_light_ctl_setup_srv_t ctl_setup_server = {
    .rsp_ctrl.get_auto_rsp = ESP_BLE_MESH_SERVER_AUTO_RSP,
    .rsp_ctrl.set_auto_rsp = ESP_BLE_MESH_SERVER_AUTO_RSP,
    .state = &ctl_state,
};
#endif  /* CONFIG_MESH_MODEL_CTL_SRV */

static esp_ble_mesh_model_t root_models[] = {
    ESP_BLE_MESH_MODEL_CFG_SRV(&config_server),
    ESP_BLE_MESH_MODEL_GEN_ONOFF_SRV(&onoff_pub, &onoff_server),
#ifdef CONFIG_MESH_MODEL_HSL_SRV
    ESP_BLE_MESH_MODEL_LIGHT_HSL_SRV(&hsl_pub, &hsl_server),
    ESP_BLE_MESH_MODEL_LIGHT_HSL_SETUP_SRV(&hsl_setup_pub, &hsl_setup_server),
#endif /* CONFIG_MESH_MODEL_HSL_SRV */
#ifdef CONFIG_MESH_MODEL_CTL_SRV
    ESP_BLE_MESH_MODEL_LIGHT_CTL_SRV(&ctl_pub, &ctl_server),
    ESP_BLE_MESH_MODEL_LIGHT_CTL_SETUP_SRV(&ctl_setup_pub, &ctl_setup_server),
#endif /* CONFIG_MESH_MODEL_CTL_SRV */
};

#ifdef CONFIG_MESH_MODEL_VND_SRV
static esp_ble_mesh_model_op_t vnd_server_op[] = {
    ESP_BLE_MESH_MODEL_OP(MESH_MODEL_OP_ATTR_SET, 1),
    ESP_BLE_MESH_MODEL_OP(MESH_MODEL_OP_ATTR_GET, 1),
    ESP_BLE_MESH_MODEL_OP_END,
};
 
static esp_ble_mesh_model_t vnd_models[] = {
    ESP_BLE_MESH_VENDOR_MODEL(CID_COMPANY, MESH_VENDOR_MODEL_ID_SERVER, vnd_server_op, NULL, NULL),
};
#endif  /* CONFIG_MESH_MODEL_VND_SRV */

static esp_ble_mesh_elem_t elements[] = {
#ifdef CONFIG_MESH_MODEL_VND_SRV    
    ESP_BLE_MESH_ELEMENT(0, root_models, vnd_models),
#else 
    ESP_BLE_MESH_ELEMENT(0, root_models, ESP_BLE_MESH_MODEL_NONE),    
#endif  /* CONFIG_MESH_MODEL_VND_SRV */  
};

static esp_ble_mesh_comp_t composition = {
    .cid = CID_COMPANY,
    .elements = elements,
    .element_count = ARRAY_SIZE(elements),
};

/* Disable OOB security for SILabs Android app */
static esp_ble_mesh_prov_t provision = {
    .uuid           = dev_uuid,
    .static_val     = NULL,
    .static_val_len = 0,
    .output_size    = 0,                        // not support output OOB
    .output_actions = ESP_BLE_MESH_NO_OUTPUT,
    .input_size     = 0,                        // not support Input OOB
    .input_actions  = ESP_BLE_MESH_NO_INPUT,
};

//==============================================================================================
//==============================================================================================
esp_err_t local_operation(uint16_t app_idx)
{
    uint16_t primary_element_addr = esp_ble_mesh_get_primary_element_address();
    #ifdef TAG   
    ESP_LOGI(TAG, "app_idx: %d, primary element address: %d", app_idx, primary_element_addr);
    #endif

    ESP_ERROR_CHECK(esp_ble_mesh_model_subscribe_group_addr(primary_element_addr, BLE_MESH_CID_NVAL, ESP_BLE_MESH_MODEL_ID_GEN_ONOFF_SRV, MESH_LIGHT_GROUP_ADDR));
    ESP_ERROR_CHECK(esp_ble_mesh_model_subscribe_group_addr(primary_element_addr, BLE_MESH_CID_NVAL, ESP_BLE_MESH_MODEL_ID_GEN_ONOFF_SRV, MESH_VND_GROUP_ADDR));
    ESP_ERROR_CHECK(esp_ble_mesh_node_bind_app_key_to_local_model(primary_element_addr, BLE_MESH_CID_NVAL, ESP_BLE_MESH_MODEL_ID_GEN_ONOFF_SRV, app_idx));

#ifdef CONFIG_MESH_MODEL_VND_SRV    
    ESP_ERROR_CHECK(esp_ble_mesh_model_subscribe_group_addr(primary_element_addr, CID_COMPANY, MESH_VENDOR_MODEL_ID_SERVER, MESH_VND_GROUP_ADDR));
    ESP_ERROR_CHECK(esp_ble_mesh_node_bind_app_key_to_local_model(primary_element_addr, CID_COMPANY, MESH_VENDOR_MODEL_ID_SERVER, app_idx));
#endif

#ifdef CONFIG_MESH_MODEL_HSL_SRV
    ESP_ERROR_CHECK(esp_ble_mesh_model_subscribe_group_addr(primary_element_addr, BLE_MESH_CID_NVAL, ESP_BLE_MESH_MODEL_ID_LIGHT_HSL_SRV, MESH_LIGHT_GROUP_ADDR));
    ESP_ERROR_CHECK(esp_ble_mesh_node_bind_app_key_to_local_model(primary_element_addr, BLE_MESH_CID_NVAL, ESP_BLE_MESH_MODEL_ID_LIGHT_HSL_SRV, app_idx));

    ESP_ERROR_CHECK(esp_ble_mesh_node_bind_app_key_to_local_model(primary_element_addr, BLE_MESH_CID_NVAL, ESP_BLE_MESH_MODEL_ID_LIGHT_HSL_SETUP_SRV, app_idx));
#endif

#ifdef CONFIG_MESH_MODEL_CTL_SRV
    ESP_ERROR_CHECK(esp_ble_mesh_model_subscribe_group_addr(primary_element_addr, BLE_MESH_CID_NVAL, ESP_BLE_MESH_MODEL_ID_LIGHT_CTL_SRV, MESH_LIGHT_GROUP_ADDR));
    ESP_ERROR_CHECK(esp_ble_mesh_node_bind_app_key_to_local_model(primary_element_addr, BLE_MESH_CID_NVAL, ESP_BLE_MESH_MODEL_ID_LIGHT_CTL_SRV, app_idx));

    ESP_ERROR_CHECK(esp_ble_mesh_node_bind_app_key_to_local_model(primary_element_addr, BLE_MESH_CID_NVAL, ESP_BLE_MESH_MODEL_ID_LIGHT_CTL_SETUP_SRV, app_idx));
#endif
 
    return ESP_OK;
}

static void prov_complete(uint16_t net_idx, uint16_t addr, uint8_t flags, uint32_t iv_index)
{
#ifdef TAG   
    ESP_LOGI(TAG, "net_idx 0x%02x, addr 0x%04x", net_idx, addr);
    ESP_LOGI(TAG, "flags 0x%02x, iv_index 0x%08lx", flags, iv_index);
#endif  
    if (store.addr > 0 && store.addr != addr) {  // 判断是否配网成功了！
        store.update = UPDATE_CLEAR;  // 重置清网更新
    }    
}

static void ble_mesh_provisioning_cb(esp_ble_mesh_prov_cb_event_t event, esp_ble_mesh_prov_cb_param_t *param)
{
    switch (event) {    
    case ESP_BLE_MESH_PROV_REGISTER_COMP_EVT:
        #ifdef TAG 
        ESP_LOGI(TAG, "ESP_BLE_MESH_PROV_REGISTER_COMP_EVT, err_code %d", param->prov_register_comp.err_code);
        #endif
        break;
    case ESP_BLE_MESH_NODE_PROV_ENABLE_COMP_EVT:
        #ifdef TAG 
        ESP_LOGI(TAG, "ESP_BLE_MESH_NODE_PROV_ENABLE_COMP_EVT, err_code %d", param->node_prov_enable_comp.err_code);
        #endif
        break;
    case ESP_BLE_MESH_NODE_PROV_LINK_OPEN_EVT:   
        #ifdef TAG 
        ESP_LOGI(TAG, "ESP_BLE_MESH_NODE_PROV_LINK_OPEN_EVT, bearer %s",
            param->node_prov_link_open.bearer == ESP_BLE_MESH_PROV_ADV ? "PB-ADV" : "PB-GATT");
        #endif  
        xEventGroupClearBits(xEvent, 0xFF);  // 先清空事件
        xEventGroupSetBits(xEvent, NESH_PROV_OPEN_EVT);   // 正在配网   
        break;
    case ESP_BLE_MESH_NODE_PROV_LINK_CLOSE_EVT:
        #ifdef TAG 
        ESP_LOGI(TAG, "ESP_BLE_MESH_NODE_PROV_LINK_CLOSE_EVT, bearer %s",
            param->node_prov_link_close.bearer == ESP_BLE_MESH_PROV_ADV ? "PB-ADV" : "PB-GATT");
        #endif
        xEventGroupSetBits(xEvent, NESH_PROV_CLOSE_EVT);   // 断开连接
        break;        
    case ESP_BLE_MESH_NODE_PROV_COMPLETE_EVT:
        #ifdef TAG   
        ESP_LOGI(TAG, "ESP_BLE_MESH_NODE_PROV_COMPLETE_EVT");
        #endif
        prov_complete(param->node_prov_complete.net_idx, param->node_prov_complete.addr,
                      param->node_prov_complete.flags, param->node_prov_complete.iv_index);
        break;
    case ESP_BLE_MESH_NODE_PROV_RESET_EVT:
        #ifdef TAG   
        ESP_LOGI(TAG, "ESP_BLE_MESH_NODE_PROV_RESET_EVT");
        #endif
        store.update = UPDATE_CLEAR;  // 重置清网更新
        break;
#ifdef TAG           
    case ESP_BLE_MESH_NODE_SET_UNPROV_DEV_NAME_COMP_EVT:
        ESP_LOGI(TAG, "ESP_BLE_MESH_NODE_SET_UNPROV_DEV_NAME_COMP_EVT, err_code %d", param->node_set_unprov_dev_name_comp.err_code);
        break;
    case ESP_BLE_MESH_NODE_ADD_LOCAL_NET_KEY_COMP_EVT:
        ESP_LOGI(TAG, "ESP_BLE_MESH_NODE_ADD_LOCAL_NET_KEY_COMP_EVT");
        break;
    case ESP_BLE_MESH_NODE_BIND_APP_KEY_TO_MODEL_COMP_EVT:
        ESP_LOGI(TAG, "ESP_BLE_MESH_NODE_BIND_APP_KEY_TO_MODEL_COMP_EVT, model_id = 0x%04x", param->node_bind_app_key_to_model_comp.model_id);
        break;
    case ESP_BLE_MESH_MODEL_SUBSCRIBE_GROUP_ADDR_COMP_EVT:
        ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_SUBSCRIBE_GROUP_ADDR_COMP_EVT, err_code %d, element_addr 0x%04x, company_id 0x%04x, model_id 0x%04x, group_addr 0x%04x",
                 param->model_sub_group_addr_comp.err_code, param->model_sub_group_addr_comp.element_addr,
                 param->model_sub_group_addr_comp.company_id, param->model_sub_group_addr_comp.model_id,
                 param->model_sub_group_addr_comp.group_addr);
        break;
    case ESP_BLE_MESH_PROXY_SERVER_CONNECTED_EVT:
        ESP_LOGI(TAG, "ESP_BLE_MESH_PROXY_SERVER_CONNECTED_EVT");
        break;
    case ESP_BLE_MESH_PROXY_SERVER_DISCONNECTED_EVT:
        ESP_LOGI(TAG, "ESP_BLE_MESH_PROXY_SERVER_DISCONNECTED_EVT");
        break;     
#endif   
    case ESP_BLE_MESH_DEINIT_MESH_COMP_EVT:
        #ifdef TAG   
        ESP_LOGI(TAG, "ESP_BLE_MESH_DEINIT_MESH_COMP_EVT");
        #endif
        xEventGroupSetBits(xEvent, NESH_PROV_DEINIT_EVT);   // 删除
        break;            
    default:
        #ifdef TAG   
        ESP_LOGI(TAG, "<ble_mesh_provisioning_cb>, default->event = %d", event);
        #endif
        break;
    }
}
 
static void ble_mesh_config_server_cb(esp_ble_mesh_cfg_server_cb_event_t event, esp_ble_mesh_cfg_server_cb_param_t *param)
{
    if (event == ESP_BLE_MESH_CFG_SERVER_STATE_CHANGE_EVT) {
        switch (param->ctx.recv_op) {
        case ESP_BLE_MESH_MODEL_OP_APP_KEY_ADD:
            #ifdef TAG   
            ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_OP_APP_KEY_ADD");
            ESP_LOGI(TAG, "net_idx 0x%04x, app_idx 0x%04x",
                param->value.state_change.appkey_add.net_idx,
                param->value.state_change.appkey_add.app_idx);
            ESP_LOG_BUFFER_HEX("AppKey", param->value.state_change.appkey_add.app_key, 16);
            #endif
            memcpy(store.app_key, param->value.state_change.appkey_add.app_key, 16);
            store.net_idx = param->value.state_change.appkey_add.net_idx;
            store.app_idx = param->value.state_change.appkey_add.app_idx;
            store.update = UPDATE_ADD;  // 更新
            // local bind AppKEY and Subcribe Group Address
            local_operation(param->value.state_change.appkey_add.app_idx);
            break;
        case ESP_BLE_MESH_MODEL_OP_APP_KEY_UPDATE:
            #ifdef TAG   
            ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_OP_APP_KEY_UPDATE");
            #endif
            memcpy(store.app_key, param->value.state_change.appkey_update.app_key, 16);
            store.net_idx = param->value.state_change.appkey_update.net_idx;
            store.app_idx = param->value.state_change.appkey_update.app_idx;
            store.update = UPDATE_ADD;  // 更新
            break;
        case ESP_BLE_MESH_MODEL_OP_APP_KEY_DELETE:
            #ifdef TAG   
            ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_OP_APP_KEY_DELETE");
            #endif
            store.update = UPDATE_CLEAR;  // 重置清网更新
            break;
        case ESP_BLE_MESH_MODEL_OP_MODEL_APP_BIND:  // 配网完成！！！
            #ifdef TAG   
            ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_OP_MODEL_APP_BIND");
            ESP_LOGI(TAG, "elem_addr 0x%04x, app_idx 0x%04x, cid 0x%04x, mod_id 0x%04x",
                param->value.state_change.mod_app_bind.element_addr,
                param->value.state_change.mod_app_bind.app_idx,
                param->value.state_change.mod_app_bind.company_id,
                param->value.state_change.mod_app_bind.model_id);
            #endif   
            store.addr = param->value.state_change.mod_app_bind.element_addr;  // 分配到的单播地址
            store.update = UPDATE_ADD;  // 更新   
            xEventGroupSetBits(xEvent, NESH_PROV_COMP_EVT);   // 配网完成
            break;
        case ESP_BLE_MESH_MODEL_OP_NET_KEY_ADD:
            memcpy(store.net_key, param->value.state_change.netkey_add.net_key, 16);
            store.net_idx = param->value.state_change.netkey_add.net_idx;
            store.update = UPDATE_ADD;  // 更新
            #ifdef TAG   
            ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_OP_NET_KEY_ADD, net_idx = %d", store.net_idx);
            #endif
            break;
        case ESP_BLE_MESH_MODEL_OP_NET_KEY_UPDATE:
            memcpy(store.net_key, param->value.state_change.netkey_update.net_key, 16);
            store.net_idx = param->value.state_change.netkey_update.net_idx;
            store.update = UPDATE_ADD;  // 更新
            #ifdef TAG   
            ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_OP_NET_KEY_UPDATE, net_idx = %d", store.net_idx);
            #endif
            break;
        case ESP_BLE_MESH_MODEL_OP_NET_KEY_DELETE:
            #ifdef TAG   
            ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_OP_NET_KEY_DELETE");
            #endif
            store.update = UPDATE_CLEAR;  // 重置清网更新
            break;
 #ifdef TAG               
        case ESP_BLE_MESH_MODEL_OP_MODEL_PUB_SET:
            ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_OP_MODEL_PUB_SET");
            ESP_LOGI(TAG, "elem_addr 0x%04x, pub_addr 0x%04x, cid 0x%04x, mod_id 0x%04x",
                            param->value.state_change.mod_pub_set.element_addr,
                            param->value.state_change.mod_pub_set.pub_addr,
                            param->value.state_change.mod_pub_set.company_id,
                            param->value.state_change.mod_pub_set.model_id); 
            break;
        case ESP_BLE_MESH_MODEL_OP_MODEL_SUB_ADD:
            ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_OP_MODEL_SUB_ADD");
            ESP_LOGI(TAG, "elem_addr 0x%04x, sub_addr 0x%04x, cid 0x%04x, mod_id 0x%04x",
                     param->value.state_change.mod_sub_add.element_addr,
                     param->value.state_change.mod_sub_add.sub_addr,
                     param->value.state_change.mod_sub_add.company_id,
                     param->value.state_change.mod_sub_add.model_id);
            break;
        case ESP_BLE_MESH_MODEL_OP_MODEL_SUB_DELETE:
            ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_OP_MODEL_SUB_DELETE");
            break;

        case ESP_BLE_MESH_MODEL_OP_HEARTBEAT_PUB_SET:
            ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_OP_HEARTBEAT_PUB_SET");
            break;
        case ESP_BLE_MESH_MODEL_OP_HEARTBEAT_SUB_SET:
            ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_OP_HEARTBEAT_SUB_SET");
            break;
        default:
            ESP_LOGI(TAG, "[ble_mesh_config_server_cb], default->event = %d", event);
            break;
        }
#else             
        default:
            break;
        }
#endif        
    }
}

// 发布消息的CTX
static esp_ble_mesh_msg_ctx_t mesh_pulish_msg_ctx(void)
{
    esp_ble_mesh_msg_ctx_t ctx;
    ctx.net_idx  = store.net_idx;
    ctx.app_idx  = store.app_idx;
    ctx.addr     = PROV_OWN_ADDR;  /** Root Remote address. */
    ctx.send_rel = false;
    ctx.send_ttl = 2;
    return ctx;
}

// 更新状态
void ble_mesh_update_onoff_state(bool onoff)
{
    esp_ble_mesh_server_state_value_t state = { 0 };
    state.gen_onoff.onoff = onoff;
    esp_ble_mesh_server_model_update_state(onoff_server.model, ESP_BLE_MESH_GENERIC_ONOFF_STATE, &state);
} 

// 响应状态
void ble_mesh_send_onoff_status(esp_ble_mesh_msg_ctx_t ctx, uint8_t onoff)
{
    esp_ble_mesh_server_model_send_msg(onoff_server.model, &ctx, ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_STATUS, 1, (uint8_t *)&onoff);
}

bool ble_mesh_send_onoff_message(bool onoff)
{
    if (xEvent == NULL || esp_ble_mesh_node_is_provisioned() == false) return false;
 
    ble_mesh_send_onoff_status(mesh_pulish_msg_ctx(), onoff);

    EventBits_t uxBits = xEventGroupWaitBits(xEvent, NESH_SEND_COMP_EVT | NESH_SEND_ERROR_EVT, pdTRUE, pdFALSE, 300);
    if (uxBits & NESH_SEND_COMP_EVT) {  
        return true;
    }  
    return false;
}
 
// 响应状态
void ble_mesh_send_vendor_status(esp_ble_mesh_msg_ctx_t ctx, uint8_t *data, uint8_t len)
{
    #ifdef TAG
    ESP_LOGI(TAG, "ble_mesh_send_vendor_status addr = 0x%04x", ctx.addr);
    #endif
    esp_ble_mesh_server_model_send_msg(&vnd_models[0], &ctx, MESH_MODEL_OP_ATTR_STATUS, len, data);
}

bool ble_mesh_send_vendor_message(uint8_t *data, uint8_t len)
{
    if (xEvent == NULL || esp_ble_mesh_node_is_provisioned() == false) return false;

    #ifdef TAG
    esp_log_buffer_hex("send_vendor", data, len);
    #endif
    ble_mesh_send_vendor_status(mesh_pulish_msg_ctx(), data, len);

    EventBits_t uxBits = xEventGroupWaitBits(xEvent, NESH_SEND_COMP_EVT | NESH_SEND_ERROR_EVT, pdTRUE, pdFALSE, 300);
    if (uxBits & NESH_SEND_COMP_EVT) {  
        return true;
    }  
    return false;
}


bool ble_mesh_node_is_provisioned(void)
{
    if (xEvent == NULL) return true;
    return esp_ble_mesh_node_is_provisioned();
}

#if 0
void ble_mesh_change_led_state(esp_ble_mesh_model_t *model, esp_ble_mesh_msg_ctx_t *ctx, uint8_t onoff)
{
    uint16_t primary_addr = esp_ble_mesh_get_primary_element_address();
    uint8_t  elem_count = esp_ble_mesh_get_element_count();

    if (ESP_BLE_MESH_ADDR_IS_UNICAST(ctx->recv_dst)) {
        for (uint8_t i = 0; i < elem_count; i++) {
            if (ctx->recv_dst == (primary_addr + i)) {
               
            }
        }
    } else if (ESP_BLE_MESH_ADDR_IS_GROUP(ctx->recv_dst)) {
        if (esp_ble_mesh_is_model_subscribed_to_group(model, ctx->recv_dst)) {
             
        }
    } else if (ctx->recv_dst == 0xFFFF) {
         
    }
}
#endif
 
static void ble_mesh_generic_server_cb(esp_ble_mesh_generic_server_cb_event_t event, esp_ble_mesh_generic_server_cb_param_t *param)
{
    #ifdef TAG
    ESP_LOGI(TAG, "<ble_mesh_generic_server_cb>, event 0x%02x, opcode 0x%06lx, src 0x%04x, dst 0x%04x",
             event, param->ctx.recv_op, param->ctx.addr, param->ctx.recv_dst);
    #endif

    mesh_transfer_t queue = { 0 };  

    switch (event) {
    case ESP_BLE_MESH_GENERIC_SERVER_STATE_CHANGE_EVT:  /*!< ESP_BLE_MESH_SERVER_AUTO_RSP */ 
        #ifdef TAG
        ESP_LOGI(TAG, "ESP_BLE_MESH_GENERIC_SERVER_STATE_CHANGE_EVT");
        #endif
        switch (param->ctx.recv_op) {
        case ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET:
        case ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK:
            #ifdef TAG
            ESP_LOGI(TAG, "onoff 0x%02x", param->value.state_change.onoff_set.onoff);
            #endif
            queue.ack = false;  // 自动响应了，这里就不响应！
            queue.len = 1;
            queue.data[0] = param->value.state_change.onoff_set.onoff;
            break;
        default:
            break;
        }
        break;
    case ESP_BLE_MESH_GENERIC_SERVER_RECV_GET_MSG_EVT:  /*!< ESP_BLE_MESH_SERVER_RSP_BY_APP */ 
        #ifdef TAG
        ESP_LOGI(TAG, "ESP_BLE_MESH_GENERIC_SERVER_RECV_GET_MSG_EVT");
        #endif
        esp_ble_mesh_gen_onoff_srv_t *srv = param->model->user_data;
        switch (param->ctx.recv_op) {
        case ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_GET:
            #ifdef TAG
            ESP_LOGI(TAG, "get_onoff: 0x%02x", srv->state.onoff);
            #endif
            queue.ack = true;
            queue.len = 1;
            queue.data[0] = srv->state.onoff;
            break;
        default:
            break;
        }
        break;
    case ESP_BLE_MESH_GENERIC_SERVER_RECV_SET_MSG_EVT: {
        #ifdef TAG
        ESP_LOGI(TAG, "ESP_BLE_MESH_GENERIC_SERVER_RECV_SET_MSG_EVT");
        #endif
        break;
    }
    default:
        #ifdef TAG
        ESP_LOGW(TAG, "Unknown Generic Server event 0x%02x", event);
        #endif
        break;
    }

    if (queue.ack || queue.len) { 
        memcpy(&queue.ctx, &param->ctx, sizeof(esp_ble_mesh_msg_ctx_t));
        xQueueSend(xQueue, &queue, 0);   
    }
}


#ifdef CONFIG_MESH_MODEL_HSL_SRV
void ble_mesh_send_hsl_status(esp_ble_mesh_msg_ctx_t ctx, uint16_t hue, uint16_t saturation, uint16_t lightness)
{
    esp_ble_mesh_state_change_light_hsl_set_t hsl_set;
    hsl_set.lightness = lightness;      /*!< The value of Light HSL Lightness state */
    hsl_set.hue = hue;                  /*!< The value of Light HSL Hue state */
    hsl_set.saturation = saturation;    /*!< The value of Light HSL Saturation state */
    // if (buf->len != 6 && buf->len != 7)  // [W] BLE_MESH: Invalid Light HSL Status length 
    esp_ble_mesh_server_model_send_msg(hsl_server.model, &ctx, ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_STATUS, sizeof(esp_ble_mesh_state_change_light_hsl_set_t), (uint8_t *)&hsl_set);
}

bool ble_mesh_send_hsl_message(uint16_t hue, uint16_t saturation, uint16_t lightness)
{
    if (xEvent == NULL || esp_ble_mesh_node_is_provisioned() == false) return false;

    ble_mesh_send_hsl_status(mesh_pulish_msg_ctx(), hue, saturation, lightness);
 
    EventBits_t uxBits = xEventGroupWaitBits(xEvent, NESH_SEND_COMP_EVT | NESH_SEND_ERROR_EVT, pdTRUE, pdFALSE, 300);
    if (uxBits & NESH_SEND_COMP_EVT) {  
        return true;
    }  
    return false;
}
#endif

#ifdef CONFIG_MESH_MODEL_CTL_SRV
void ble_mesh_send_ctl_status(esp_ble_mesh_msg_ctx_t ctx, uint16_t lightness, uint16_t temperature)
{
    temperature += BLE_MESH_TEMPERATURE_MIN;
#if 0    
    uint16_t data[5];
    data[0] = lightness;     // present_ctl_lightness
    data[1] = temperature;   // present_ctl_temperature
    data[2] = lightness;     // target_ctl_lightness
    data[3] = temperature;   // target_ctl_temperature
    data[4] = 0x00;          // remain_time
    // if (buf->len != 4 && buf->len != 9)  // // [W] BLE_MESH: Invalid Light CTL Status length 
    esp_ble_mesh_server_model_send_msg(ctl_server.model, &ctx, ESP_BLE_MESH_MODEL_OP_LIGHT_CTL_STATUS, 9, (uint8_t *)&ctl_set); 
#else 
    esp_ble_mesh_state_change_light_ctl_set_t ctl_set;
    ctl_set.lightness = lightness;       /*!< The value of Light CTL Lightness state */
    ctl_set.temperature = temperature;   /*!< The value of Light CTL Temperature state */
    ctl_set.delta_uv = 0;                /*!< The value of Light CTL Delta UV state */
    // if (buf->len != 4 && buf->len != 9)  // // [W] BLE_MESH: Invalid Light CTL Status length 
    esp_ble_mesh_server_model_send_msg(ctl_server.model, &ctx, ESP_BLE_MESH_MODEL_OP_LIGHT_CTL_STATUS, 4, (uint8_t *)&ctl_set);
#endif   
}

bool ble_mesh_send_ctl_message(uint16_t lightness, uint16_t temperature)
{
    if (xEvent == NULL || esp_ble_mesh_node_is_provisioned() == false) return false;
    
    ble_mesh_send_ctl_status(mesh_pulish_msg_ctx(), lightness, temperature);

    EventBits_t uxBits = xEventGroupWaitBits(xEvent, NESH_SEND_COMP_EVT | NESH_SEND_ERROR_EVT, pdTRUE, pdFALSE, 300);
    if (uxBits & NESH_SEND_COMP_EVT) {  
        return true;
    }  
    return false;
}
#endif  
#ifdef CONFIG_MESH_MODEL_LIGHT_SRV
static void ble_mesh_lighting_server_cb(esp_ble_mesh_lighting_server_cb_event_t event, esp_ble_mesh_lighting_server_cb_param_t *param)
{
 
    #ifdef TAG
    ESP_LOGI(TAG, "event 0x%02x, opcode 0x%06lx, src 0x%04x, dst 0x%04x",
             event, param->ctx.recv_op, param->ctx.addr, param->ctx.recv_dst);
    #endif

    mesh_transfer_t queue = { 0 };

    switch (event) {
    case ESP_BLE_MESH_LIGHTING_SERVER_STATE_CHANGE_EVT:  /*!< ESP_BLE_MESH_SERVER_AUTO_RSP */ 
        switch (param->ctx.recv_op) {    
        case ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_SET:
        case ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_SET_UNACK: {
            #ifdef TAG
            ESP_LOGI(TAG, "LIGHT_HSL_SET: %d %d %d", 
                    param->value.state_change.hsl_set.hue,
                    param->value.state_change.hsl_set.saturation,
                    param->value.state_change.hsl_set.lightness);
            #endif
            uint16_t hsl_data[3];
            hsl_data[0] = param->value.state_change.hsl_set.hue;
            hsl_data[1] = param->value.state_change.hsl_set.saturation;
            hsl_data[2] = param->value.state_change.hsl_set.lightness;
            queue.ack = false;
            queue.len = 6;
            memcpy(queue.data, hsl_data, queue.len);
            break;
        }  
        /* SIG MESH MODEL Light CTL state 翻译：https://www.cnblogs.com/gancai/p/12309912.html */ 
        case ESP_BLE_MESH_MODEL_OP_LIGHT_CTL_SET:
        case ESP_BLE_MESH_MODEL_OP_LIGHT_CTL_SET_UNACK: {
            param->value.state_change.ctl_set.temperature -= BLE_MESH_TEMPERATURE_MIN;
            #ifdef TAG
            ESP_LOGI(TAG, "lightness %d, temperature %d, delta uv %d",
                     param->value.state_change.ctl_set.lightness,
                     param->value.state_change.ctl_set.temperature,
                     param->value.state_change.ctl_set.delta_uv);
            #endif
            uint16_t ctl_data[3];
            ctl_data[0] = param->value.state_change.ctl_set.lightness;
            ctl_data[1] = param->value.state_change.ctl_set.temperature;
            ctl_data[2] = param->value.state_change.ctl_set.delta_uv;
            queue.ack = false;
            queue.len = 6;
            memcpy(queue.data, ctl_data, queue.len);
            break;
        }  
        default:
            #ifdef TAG
            ESP_LOGW(TAG, "ESP_BLE_MESH_LIGHTING_SERVER_STATE_CHANGE_EVT, default: 0x%06lx", param->ctx.recv_op);
            #endif
            break;
        }
        break;
    case ESP_BLE_MESH_LIGHTING_SERVER_RECV_GET_MSG_EVT:  /*!< ESP_BLE_MESH_SERVER_RSP_BY_APP */ 
        switch (param->ctx.recv_op) {    
        case ESP_BLE_MESH_MODEL_OP_LIGHT_HSL_GET:
        case ESP_BLE_MESH_MODEL_OP_LIGHT_CTL_GET: /*!< ESP_BLE_MESH_SERVER_RSP_BY_APP */ 
            #ifdef TAG
            ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_OP_LIGHT_CTL_GET");
            #endif
            queue.ack = true;
            queue.len = 0;  
            break;
        default:
            break;   
        }  
        break;  
    default:
        ESP_LOGE(TAG, "Unknown Lighting Server event 0x%02x", event);
        break;
    }

    if (queue.ack || queue.len) { 
        memcpy(&queue.ctx, &param->ctx, sizeof(esp_ble_mesh_msg_ctx_t));
        xQueueSend(xQueue, &queue, 0);   
    }
}
#endif

#ifdef CONFIG_MESH_MODEL_VND_SRV
static void ble_mesh_custom_model_cb(esp_ble_mesh_model_cb_event_t event, esp_ble_mesh_model_cb_param_t *param)
{
    switch (event) {
    case ESP_BLE_MESH_MODEL_OPERATION_EVT:
        #ifdef TAG
        ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_OPERATION_EVT->opcode 0x%06lx; net_idx = 0x%04x; app_idx =0x%04x", 
                        param->model_operation.ctx->recv_op, 
                        param->model_operation.ctx->net_idx,
                        param->model_operation.ctx->app_idx);
        #endif
        if (param->model_operation.opcode == MESH_MODEL_OP_ATTR_SET || \
            param->model_operation.opcode == MESH_MODEL_OP_ATTR_GET) {
            mesh_transfer_t queue;  
            memcpy(queue.data, param->model_operation.msg, param->model_operation.length);
            memcpy(&queue.ctx, param->model_operation.ctx, sizeof(esp_ble_mesh_msg_ctx_t));
            queue.len = param->model_operation.length;
            queue.ack = true;
            xQueueSend(xQueue, &queue, 0);   
        }
        break;
    case ESP_BLE_MESH_MODEL_SEND_COMP_EVT:
        if (param->model_send_comp.err_code) {
            xEventGroupSetBits(xEvent, NESH_SEND_ERROR_EVT);   
            #ifdef TAG
            ESP_LOGE(TAG, "Failed to send message 0x%06lx", param->model_send_comp.opcode);
            #endif
        } else {
            xEventGroupSetBits(xEvent, NESH_SEND_COMP_EVT);   
            #ifdef TAG
            ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_SEND_COMP_EVT->Send 0x%06lx", param->model_send_comp.opcode);
            #endif
        }
        break;
#ifdef TAG        
    case ESP_BLE_MESH_MODEL_PUBLISH_UPDATE_EVT: { 
        ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_PUBLISH_UPDATE_EVT...");
        #if 0
        net_buf_simple_init(param->model_publish_update.model->pub->msg, 0);
        net_buf_simple_add_be16(param->model_publish_update.model->pub->msg, ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_STATUS);
        net_buf_simple_add_u8(param->model_publish_update.model->pub->msg, onoff_server.state.onoff);
        #endif
        break;
    }
    case ESP_BLE_MESH_SERVER_MODEL_UPDATE_STATE_COMP_EVT:   /*!< Server models update state value completion event */
        ESP_LOGI(TAG, "ESP_BLE_MESH_SERVER_MODEL_UPDATE_STATE_COMP_EVT...");
        break;
    default:
        ESP_LOGI(TAG, "[ble_mesh_custom_model_cb], default->event = %d", event);
        break;
#else 
    default:
        break;
#endif        
    }
}
#endif

// 立刻重置！ 
void ble_mesh_node_delete_reset(bool prov_enable)
{
    #ifdef TAG
    ESP_LOGI(TAG, "ble_mesh_node_delete_reset...");
    #endif
    if (xEvent == NULL) return;
    memset(&store, 0, sizeof(mesh_nvs_store_t));  // 全部清空！
    ble_mesh_nvs_store(&store, sizeof(mesh_nvs_store_t), NVS_MODE_WRITE);  // 存储
    if (prov_enable) {
        esp_ble_mesh_node_local_reset(); // 重置节点
        esp_ble_mesh_node_prov_enable(ESP_BLE_MESH_PROV_ADV | ESP_BLE_MESH_PROV_GATT); 
    } else {  // 重置&注销MESH
        esp_ble_mesh_deinit_param_t param;
        param.erase_flash = true;
        esp_ble_mesh_deinit(&param);
        xEventGroupWaitBits(xEvent, NESH_PROV_DEINIT_EVT, pdFALSE, pdFALSE, 1000);  // 等待删除完成
    }
}

static ble_mesh_recv_callback_t ble_mesh_recv_callback = NULL;
void ble_mesh_register_callback(ble_mesh_recv_callback_t cb)
{
    if (cb) {
        ble_mesh_recv_callback = cb;
    }
}

void ble_mesh_handeler_task(void *arg)
{
    mesh_transfer_t msg;

    while (1) {

        if (xQueueReceive(xQueue, &msg, 1000) == pdTRUE) {  // portMAX_DELAY
            if (ble_mesh_recv_callback) {
                ble_mesh_recv_callback(msg);
            }
        }

        if (store.update > 0x00) { // 更新
            if (store.update == UPDATE_CLEAR) {  // 是清除更新，使能重新配网
                ble_mesh_node_delete_reset(true);
            } else {
                store.update = 0x00;
                ble_mesh_nvs_store(&store, sizeof(mesh_nvs_store_t), NVS_MODE_WRITE);  // 存储
            }
        }
 
        /* 配网过程检测，看是否配网成功了 */
        EventBits_t uxBits = xEventGroupWaitBits(xEvent, NESH_PROV_CLOSE_EVT, pdFALSE, pdFALSE, 0);
        if (uxBits & NESH_PROV_CLOSE_EVT) {  // 断开链接了，再判断是否绑定KEY是否成功！
            uxBits = xEventGroupWaitBits(xEvent, NESH_PROV_COMP_EVT, pdTRUE, pdFALSE, 4000);  // 配网是否成功
            if (uxBits & NESH_PROV_COMP_EVT) {  // 配网成功
                #ifdef TAG
                ESP_LOGI(TAG, "NESH_PROV_COMP_OK ^_^!");
                #endif
            } else {  // 配网失败/超时了
                #ifdef TAG
                ESP_LOGI(TAG, "NESH_PROV_COMP_FAIL -_-!");
                #endif
                store.update = UPDATE_CLEAR;  // 重置清网更新
            }
            xEventGroupClearBits(xEvent, 0xFF);  // 记得清空事件
        }     
    }
}

extern uint16_t esp_read_chip_id(void);
static void ble_mesh_initialize(void)
{
    uint16_t chip_id = esp_read_chip_id();
    dev_uuid[8] = chip_id >> 8;
    dev_uuid[9] = chip_id&0xFF;
    esp_read_mac(dev_uuid + 10, ESP_MAC_BT);   

    char dev_name[20];
    sprintf(dev_name, CONFIG_PROJECT_NAME"%05d", chip_id);
#ifdef TAG    
    ESP_LOGI(TAG, "Mesh Node: "ADDRSTR"|%s\n\n", ADDR2STR(dev_uuid + 10), dev_name); 
#endif
    ESP_ERROR_CHECK(esp_ble_mesh_register_prov_callback(ble_mesh_provisioning_cb));
    ESP_ERROR_CHECK(esp_ble_mesh_register_config_server_callback(ble_mesh_config_server_cb));
    ESP_ERROR_CHECK(esp_ble_mesh_register_generic_server_callback(ble_mesh_generic_server_cb));
#ifdef CONFIG_MESH_MODEL_VND_SRV    
    ESP_ERROR_CHECK(esp_ble_mesh_register_custom_model_callback(ble_mesh_custom_model_cb));
#endif    
#ifdef CONFIG_MESH_MODEL_LIGHT_SRV 
    ESP_ERROR_CHECK(esp_ble_mesh_register_lighting_server_callback(ble_mesh_lighting_server_cb));
#endif 
    ESP_ERROR_CHECK(esp_ble_mesh_init(&provision, &composition));
    ESP_ERROR_CHECK(esp_ble_mesh_set_unprovisioned_device_name(dev_name));  

    if (esp_ble_mesh_node_is_provisioned()) {
        if ((store.app_key[0] == store.net_key[1]) && store.addr >= 0x0005) {  // 判断是否配网成功了！
            #ifdef TAG
            ESP_LOGW(TAG, "Node Provisioned already. ^_^!");
            #endif
        } else {  // fail...
            store.update = UPDATE_CLEAR;
            #ifdef TAG
            ESP_LOGW(TAG, "Node Provisioned Fail. -_-!");
            #endif
        }
    } else { // 没有配网！
        #ifdef TAG
        ESP_LOGI(TAG, "Node Provisioned Not. -_-!");
        #endif
        ESP_ERROR_CHECK(esp_ble_mesh_node_prov_enable(ESP_BLE_MESH_PROV_ADV | ESP_BLE_MESH_PROV_GATT));  
    }   
    return;
}

void ble_mesh_init(void)
{
    xEvent = xEventGroupCreate();
    xQueue = xQueueCreate(3, sizeof(mesh_transfer_t));

    ble_mesh_nvs_store(&store, sizeof(mesh_nvs_store_t), NVS_MODE_READ);
#ifdef TAG
    ESP_LOGI(TAG, "store.addr:    0x%04x", store.addr);
    ESP_LOGI(TAG, "store.net_idx: 0x%04x", store.net_idx);
    ESP_LOGI(TAG, "store.app_idx: 0x%04x", store.app_idx);
    esp_log_buffer_hex("store.net_key", store.net_key, 16);
    esp_log_buffer_hex("store.app_key", store.app_key, 16);
#endif
 
    /* Initialize the Bluetooth Mesh Subsystem */
    ble_mesh_initialize();
 
    xTaskCreatePinnedToCore(ble_mesh_handeler_task, "ble_mesh_handeler", 4 * 1024, NULL, 10, NULL, PRO_CPU_NUM);
}
#endif  /* CONFIG_BLE_MESH_NODE */
// ble-mesh 发布订阅介绍：
// https://www.jianshu.com/p/e4704af76449
// ble-mesh seq序列号的问题！
// https://github.com/espressif/esp-idf/issues/8669

